package pdi;


import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

import org.apache.commons.lang3.StringUtils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfFloat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.HOGDescriptor;

import pdi.core.Image;
import utils.MyUtils;



public class OpenCvCaller {

	private static OpenCvCaller instance = null;

	protected OpenCvCaller() {
		System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
	}

	public static OpenCvCaller getInstance() {
		if(instance == null) {
			instance = new OpenCvCaller();
		}
		return instance;
	}

	//all method that use opencv have to be inserted here
	public Mat readImage(String filePath){
		return Highgui.imread(filePath);
	}

	public void writeImage (String path, Mat img){
		Highgui.imwrite(path, img);
	}

	public void showImage(Image image) {
		Mat image_tmp = image.getMatrixRepresentation();
		BufferedImage bf = this.toBufferedImage(image_tmp);
		ImageIcon icon = new ImageIcon(bf);
        JLabel label = new JLabel(icon);
        JOptionPane.showMessageDialog(null, label);
	}
	
	public BufferedImage toBufferedImage(Mat m){
	      
		int type = BufferedImage.TYPE_BYTE_GRAY;
	      if ( m.channels() > 1 ) {
	          type = BufferedImage.TYPE_3BYTE_BGR;
	      }
	      int bufferSize = m.channels()*m.cols()*m.rows();
	      byte [] b = new byte[bufferSize];
	      m.get(0,0,b); // get all the pixels
	      BufferedImage image = new BufferedImage(m.cols(),m.rows(), type);
	      final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
	      System.arraycopy(b, 0, targetPixels, 0, b.length);  
	      
	      return image;

	  }
	
	public Image convertToGrayScale(Image colorImg){		
		
		Image ret = colorImg.clone();
		
		Mat grayScale = new Mat();
		Mat rgb = colorImg.getMatrixRepresentation();		
		
		Imgproc.cvtColor(rgb, grayScale, Imgproc.COLOR_RGB2GRAY);
		
		ret.setMatrixRepresentation(grayScale);		
		
		return ret; 
		
	}
	
	public Image medianFilter(Image img, int radius){
		Image ret = img.clone();		
		Mat dst = new Mat();
		Mat src = ret.getMatrixRepresentation();
		
		Imgproc.medianBlur(src, dst, radius);
		
		ret.setMatrixRepresentation(dst);
		
		return ret;				
	}
	
	public Image cropImage(Image img, double x, double y, double w, double h){
		Image ret = img.clone();
		int left = x > img.getWidth() ? img.getWidth() : (x < 0 ? 0 : (int)Math.abs(x) );
		int top = y > img.getHeight() ? img.getHeight() : (y < 0 ? 0 : (int)Math.abs(y) );
		int width = x + w > img.getWidth() ? (int)Math.abs(img.getWidth() - x) : (int)Math.abs(w);
		int height = y + h > img.getHeight() ? (int)Math.abs(img.getHeight() - y) : (int)Math.abs(h);
		
		
		Mat uncropped = ret.getMatrixRepresentation();
		Rect roi = new Rect(left,top,width,height);		
		Mat cropped = new Mat(uncropped, roi);
		
		ret.setMatrixRepresentation(cropped);
		ret.setFeatureVector(null);
		ret.setGroundTruth(null);
		ret.setHeight(cropped.height());
		ret.setWidth(cropped.width());
		ret.setName(MyUtils.addStringBFExt(ret.getName(), "_cropped_" + StringUtils.join(left,'_',top,'_',width,'_',height)));
		
		return ret;				
	}
	
	public double[] computeHOGFeatures(Image input, int blockWidth, int blockHeight){
		Image ret = input.clone();
	
		HOGDescriptor hog = new HOGDescriptor();				
		MatOfFloat ders = new MatOfFloat();
		MatOfPoint locs = new MatOfPoint();
						
		hog.compute(ret.getMatrixRepresentation(), ders, new Size(8, 8),new Size(blockWidth, blockHeight), locs);
		
		double[] feats = new double[ders.height()]; 
		for (int i = 0; i < ders.height(); i++) {
			 feats[i] = (double)ders.get(i, 0)[0];
			
		}
		
		return feats;
		
		
	}
	
	public Image resize(Image input, int width, int height){
		Image ret = input.clone();
		Mat dest = new Mat();
		
		Imgproc.resize(ret.getMatrixRepresentation(), dest, new Size(width,height));
		
		ret.setMatrixRepresentation(dest);
		ret.setWidth(dest.width());
		ret.setHeight(dest.height());
		
		return ret;
	}
}
